﻿using BUGCOME.Common;
using BUGCOME.Infrastructure;
using BUGCOME.Infrastructure.Attribute;
using BUGCOME.Infrastructure.IPTools;
using BUGCOME.Infrastructure.Model;
using BUGCOME.Model.Log;
using BUGCOME.ServiceCore.Services.IServices;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using NLog;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using textJson = System.Text.Json;

namespace BUGCOME.ServiceCore.Middleware
{
    /// <summary>
    /// 全局异常处理中间件
    /// 功能：统一捕获应用程序异常并返回标准化错误响应
    /// </summary>
    public class GlobalExceptionMiddleware
    {
        private readonly RequestDelegate next;                  // 下一个中间件委托
        private readonly IOperLogService operLogService;        // 操作日志服务，用于记录异常日志

        static readonly Logger Logger = LogManager.GetCurrentClassLogger(); // 日志记录器
        private readonly JsonSerializerOptions options = new()
        {
            Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,     // 允许非转义字符（如中文）
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,         // 驼峰命名序列化
            WriteIndented = true                                       // 生成缩进格式的JSON
        };

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="next">请求处理管道的下一个中间件</param>
        /// <param name="operLogService">操作日志服务实例</param>
        public GlobalExceptionMiddleware(RequestDelegate next, IOperLogService operLogService)
        {
            this.next = next;
            this.operLogService = operLogService;
        }

        /// <summary>
        /// 中间件核心处理方法
        /// </summary>
        /// <param name="context">HTTP上下文对象</param>
        /// <returns>异步任务</returns>
        public async Task Invoke(HttpContext context)
        {
            try
            {
                // 执行下一个中间件
                await next(context);
            }
            catch (Exception ex)
            {
                // 捕获异常并处理
                await HandleExceptionAsync(context, ex);
            }
        }

        /// <summary>
        /// 异常处理核心逻辑
        /// </summary>
        /// <param name="context">HTTP上下文对象</param>
        /// <param name="ex">捕获的异常对象</param>
        /// <returns>异步任务</returns>
        private async Task HandleExceptionAsync(HttpContext context, Exception ex)
        {
            LogLevel logLevel = LogLevel.Info;         // 日志级别
            int code = (int)ResultCode.GLOBAL_ERROR;   // 错误码
            string msg;                                // 错误消息
            string error = string.Empty;               // 详细错误信息
            bool notice = true;                        // 是否发送通知

            // 处理自定义异常
            if (ex is CustomException customException)
            {
                code = customException.Code;
                msg = customException.Message;
                error = customException.LogMsg;
                notice = customException.Notice;
            }
            // 处理参数异常
            else if (ex is ArgumentException)
            {
                code = (int)ResultCode.PARAM_ERROR;
                msg = ex.Message;
            }
            // 处理未知异常
            else
            {
                var knownIssue = "Exception has been thrown by the target of an invocation";
                var issueLink = string.Empty;

                // 匹配已知问题并添加解决方案链接
                if (ex.Message.Contains(knownIssue))
                {
                    issueLink = $"====请查看issue：https://gitee.com/izory/FLEXJOBERAdminNetCore/issues/I6S4DZ";
                }

                msg = $"异常原因：{ex.Message}{issueLink}";
                error = $"异常原因：{ex.Message}{issueLink}";
                logLevel = LogLevel.Error;
                context.Response.StatusCode = 500;       // 设置HTTP状态码为内部服务器错误
            }

            // 构建标准化错误响应
            ApiResult apiResult = new(code, msg);

            // 序列化响应结果（DEBUG模式下添加错误辅助信息）
            string responseResult = textJson.JsonSerializer.Serialize(apiResult, options);

            // 获取客户端IP及地理位置
            string ip = HttpContextExtension.GetClientUserIp(context);
            var ipInfo = IpTool.Search(ip);

            // 初始化操作日志实体
            OperLog operLog = new()
            {
                Status = 1,                               // 操作状态（1表示异常）
                OperIp = ip,                              // 操作IP
                OperUrl = HttpContextExtension.GetRequestUrl(context), // 请求URL
                RequestMethod = context.Request.Method,   // 请求方法
                JsonResult = responseResult,              // 响应结果
                ErrorMsg = string.IsNullOrEmpty(error) ? msg : error, // 错误信息
                OperName = HttpContextExtension.GetName(context),     // 操作人
                OperLocation = ipInfo.Province + " " + ipInfo.City,   // 操作地点
                OperTime = DateTime.Now,                  // 操作时间
                OperParam = HttpContextExtension.GetRequestValue(context, context.Request.Method) // 请求参数
            };

            // 从端点元数据中获取日志特性
            var endpoint = GetEndpoint(context);
            if (endpoint != null)
            {
                var logAttribute = endpoint.Metadata.GetMetadata<LogAttribute>();
                if (logAttribute != null)
                {
                    operLog.BusinessType = (int)logAttribute.BusinessType;  // 业务类型
                    operLog.Title = logAttribute?.Title ?? "";                    // 操作标题
                    operLog.OperParam = logAttribute.IsSaveRequestData ? operLog.OperParam : ""; // 按需保存请求参数
                    operLog.JsonResult = logAttribute.IsSaveResponseData ? operLog.JsonResult : ""; // 按需保存响应结果
                }
            }

            // 构建日志事件
            LogEventInfo ei = new(logLevel, "GlobalExceptionMiddleware", error)
            {
                Exception = ex,               // 异常对象
                Message = error               // 错误消息
            };
            ei.Properties["status"] = 1;                // 状态标记
            ei.Properties["jsonResult"] = responseResult; // 响应结果
            ei.Properties["requestParam"] = operLog.OperParam; // 请求参数
            ei.Properties["user"] = operLog.OperName;   // 操作人

            // 记录日志
            Logger.Log(ei);

            // 返回错误响应
            context.Response.ContentType = "text/json;charset=utf-8";
            await context.Response.WriteAsync(responseResult, Encoding.UTF8);

            // 组装详细错误信息
            string errorMsg = $"> 操作人：{operLog.OperName}" +
                $"\n> 操作地区：{operLog.OperIp}({operLog.OperLocation})" +
                $"\n> 操作模块：{operLog.Title}" +
                $"\n> 操作地址：{operLog.OperUrl}" +
                $"\n> 错误信息：{msg}\n\n> {error}";

            // 保存操作日志
           await operLogService.InsertOperlog(operLog);
           await operLogService.InsertOperlog(operLog);

            // 按需发送异常通知（企业微信等）
            if (!notice) return;
            WxNoticeHelper.SendMsg("系统异常", errorMsg, msgType: WxNoticeHelper.MsgType.markdown);
        }

        /// <summary>
        /// 获取当前请求的端点信息
        /// </summary>
        /// <param name="context">HTTP上下文对象</param>
        /// <returns>端点对象或null</returns>
        public static Endpoint? GetEndpoint(HttpContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            return context.Features.Get<IEndpointFeature>()?.Endpoint;
        }
    }
}
